package com.example.OnlineStorage.pojo.Response;

import lombok.SneakyThrows;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourceRegion;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.util.UriUtils;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * ApiResponse用于构建API的响应体，它包含响应的状态码、主体、头信息等。
 * 通过这个类，可以方便地组装一个符合RESTful API标准的响应体。
 *
 * @param <T> 响应体的数据类型，支持泛型以便于处理不同类型的数据。
 */
public class ApiResponse<T> {
	
	/**
	 * 存储响应头信息的映射表。
	 */
	private final Map<String, String> headers;
	private int status;
	private T body;
	private ResponseData responseData;
	
	/**
	 * 构造函数初始化headers映射表。
	 */
	public ApiResponse () {
		this.headers = new HashMap<>();
	}
	
	/**
	 * 提供一个静态方法创建一个空的ApiResponse对象，用于后续组装。
	 *
	 * @return ApiResponse的空实例。
	 */
	public static ApiResponse<Object> GetApiResponse () {
		return new ApiResponse<>();
	}
	
	/**
	 * 设置响应状态码为200 OK。
	 *
	 * @return ApiResponse实例，支持链式调用。
	 */
	public ApiResponse<T> ok () {
		this.status = 200;
		return this;
	}
	
	/**
	 * 设置响应的状态码。
	 *
	 * @param status HTTP响应状态码。
	 * @return ApiResponse实例，支持链式调用。
	 */
	public ApiResponse<T> status (int status) {
		this.status = status;
		return this;
	}
	
	/**
	 * 设置响应的主体。
	 *
	 * @param body 响应的主体数据。
	 * @return ApiResponse实例，支持链式调用。
	 */
	public ApiResponse<T> body (T body) {
		this.body = body;
		return this;
	}
	
	/**
	 * 设置二进制响应体的关键信息。
	 *
	 * @param responseData 二进制响应体的关键信息的对象。
	 * @return ApiResponse实例，支持链式调用。
	 */
	public ApiResponse<T> binBody (ResponseData responseData) {
		this.responseData = responseData;
		return this;
	}
	
	/**
	 * 添加一个响应头。
	 *
	 * @param key   头信息的键。
	 * @param value 头信息的值。
	 * @return ApiResponse实例，支持链式调用。
	 */
	public ApiResponse<T> addHeader (String key, String value) {
		this.headers.put(key, value);
		return this;
	}
	
	/**
	 * 批量添加响应头。
	 *
	 * @param headers 包含头信息的映射表。
	 * @return ApiResponse实例，支持链式调用。
	 */
	public ApiResponse<T> AddHeaders (Map<String, String> headers) {
		this.headers.putAll(headers);
		return this;
	}
	
	/**
	 * 根据当前设置构建一个ResponseEntity对象，用于HTTP响应。
	 *
	 * @return 构建好的ResponseEntity对象。
	 */
	public ResponseEntity<T> build () {
		HttpHeaders headers = new HttpHeaders();
		for (String key : this.headers.keySet()) {
			headers.add(key, this.headers.get(key));
		}
		
		return ResponseEntity.status(this.status).headers(headers).body(this.body);
	}
	
	@SneakyThrows
	public ResponseEntity<ResourceRegion> onlineBuild () {
		String range = responseData.getRange();
		String Path = responseData.getPath();
		File file = new File(Path);
		long fileSize = file.length();
		long start = 0;
		
		if (range != null && range.startsWith("bytes=")) {
			String[] ranges = range.substring("bytes=".length()).split("-");
			start = Long.parseLong(ranges[0]);
		}
		long end = Math.min(fileSize, start + responseData.getMaxChunkSize());
		Resource fileResource = new FileSystemResource(Path);
		ResourceRegion resourceRegion = new ResourceRegion(fileResource, start, end - start);
		HttpHeaders headers = new HttpHeaders();
		for (String key : this.headers.keySet()) {
			headers.add(key, this.headers.get(key));
		}
		headers.add(HttpHeaders.CONTENT_TYPE, responseData.getType());
		headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline");
		return ResponseEntity.status(206).headers(headers).body(resourceRegion);
		
	}
	
	@SneakyThrows public ResponseEntity<ResourceRegion> downloadBuild () {
		String range = responseData.getRange();
		String Path = responseData.getPath();
		File file = new File(Path);
		long fileSize = file.length();
		String utf8FileName = UriUtils.encode(responseData.getName(), StandardCharsets.UTF_8);
		HttpHeaders headers = new HttpHeaders();
		if (range != null && range.startsWith("bytes=")) {
			String[] ranges = range.substring("bytes=".length()).split("-");
			long start = Long.parseLong(ranges[0]);
			long end = ranges.length > 1 ? Long.parseLong(ranges[1]) : fileSize - 1;
			if (end >= fileSize) {
				end = fileSize - 1;
			}
			long contentLength = end - start + 1;
			headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
			headers.add(HttpHeaders.CONTENT_RANGE, String.format("bytes %s-%s/%s", start, end, fileSize));
			headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
			headers.add(HttpHeaders.ACCEPT_RANGES, "bytes");
			headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + utf8FileName + "\"");
			System.out.printf("有Range: %d\n", start);
			Resource fileResource = new FileSystemResource(Path);
			ResourceRegion resourceRegion = new ResourceRegion(fileResource, start, end - start + 1);
			return ResponseEntity.status(206).headers(headers).body(resourceRegion);
		} else {
			headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileSize));
			headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + utf8FileName + "\"");
			headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
			System.out.printf("无Range: %d\n", 0);
			Resource fileResource = new FileSystemResource(Path);
			ResourceRegion resourceRegion = new ResourceRegion(fileResource, 0, fileSize);
			return ResponseEntity.status(200).headers(headers).body(resourceRegion);
		}
	}
}